package com.lebang.medicalInquiry.service;

import com.alibaba.fastjson.JSONObject;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.domain.AlipayTradeAppPayModel;
import com.alipay.api.domain.AlipayTradeRefundApplyModel;
import com.alipay.api.domain.AlipayTradeRefundModel;
import com.alipay.api.request.AlipayTradeAppPayRequest;
import com.alipay.api.request.AlipayTradeRefundApplyRequest;
import com.alipay.api.request.AlipayTradeRefundRequest;
import com.alipay.api.response.AlipayTradeAppPayResponse;
import com.alipay.api.response.AlipayTradeRefundResponse;
import com.lebang.medicalInquiry.constant.BusinessEnum;
import com.lebang.medicalInquiry.constant.ConfigEnum;
import com.lebang.medicalInquiry.constant.ConsultationScheduleEnum;
import com.lebang.medicalInquiry.exception.BusinessException;
import com.lebang.medicalInquiry.mapper.*;
import com.lebang.medicalInquiry.model.po.*;
import com.lebang.medicalInquiry.utils.TextUtil;
import com.lebang.medicalInquiry.utils.UUIDUtil;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import com.wechat.pay.java.service.payments.app.AppService;

import com.wechat.pay.java.service.payments.app.model.Amount;
import com.wechat.pay.java.service.payments.app.model.PrepayRequest;
import com.wechat.pay.java.service.payments.app.model.PrepayResponse;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;

@Service
public class PayService {

    @Resource
    private ConfigMapper configMapper;

    @Resource
    private ConsultationSheetMapper consultationSheetMapper;

    @Resource
    private UserMapper userMapper;

    @Resource
    private ConsultationSheetScheduleMapper consultationSheetScheduleMapper;

    @Resource
    private OrderMapper orderMapper;

    /**
     * 问诊单支付  支付完的问诊单才会出现在用户的问诊单列表中
     *
     * @param consultationSheetId
     * @param uuid
     * @param type
     * @return
     */
    public Map<String, String> payService(Integer consultationSheetId, String uuid, String payType, Integer type) {
        ConsultationSheet consultationSheet = consultationSheetMapper.selectByPrimaryKey(consultationSheetId);
        if (TextUtil.isEmpty(consultationSheet)) {
            throw new BusinessException("问诊单不存在");
        }
        if (TextUtil.isEmpty(consultationSheet.getOrganizationOperationUuid())) {
            throw new BusinessException("问诊单的运营人员为空");
        }
        // 如果账单id已存在且不是待结算的状态 不用付费
        if (TextUtil.isEmpty(consultationSheet.getScheduleState())) {
            throw new BusinessException("问诊单进度状态为空");
        }
        String scheduleState = consultationSheet.getScheduleState();
        // 如果账单id已存在且不是待结算的状态 不用付费
        if (!scheduleState.equals(ConsultationScheduleEnum.settle_bill.getCode()) && !scheduleState.equals(ConsultationScheduleEnum.wait_prepay.getCode())) {
            throw new BusinessException("状态进度只有在【待预付】和【待结算】才可以创建订单");
        }
        // 不是待结算的状态 不可以退款
        if (!scheduleState.equals(ConsultationScheduleEnum.settle_bill.getCode()) && "refund".equals(payType)) {
            throw new BusinessException("状态进度只有在【待结算】才可以退款");
        }
        //当前是代预付
        if (scheduleState.equals(ConsultationScheduleEnum.wait_prepay.getCode())) {
            final Integer countUnfinish = consultationSheetMapper.countUnfinish(consultationSheet.getUserPaitentId(), consultationSheet.getOrganizationOperationUuid());
            if (countUnfinish > 0) {
                throw new BusinessException("当前还存在未结束的问诊单");
            }
            if ("pay".equals(payType)) {
                Integer count = orderMapper.countBySheetIdAndPayTypeAndStatus(consultationSheet.getId(), "pay", ConsultationScheduleEnum.wait_prepay.getCode());
                if (count > 0) {
                    throw new BusinessException("当前问诊单已支付");
                }
            }
        }
        // 当前问诊单是否已退款
        if (scheduleState.equals(ConsultationScheduleEnum.settle_bill.getCode()) && "refund".equals(payType)) {
            Integer refundCount = orderMapper.countBySheetIdAndPayTypeAndStatus(consultationSheet.getId(), "refund", ConsultationScheduleEnum.settle_bill.getCode());
            if (refundCount > 0) {
                throw new BusinessException("当前问诊单已退款");
            }
            Integer payCount = orderMapper.countBySheetIdAndPayTypeAndStatus(consultationSheet.getId(), "pay", ConsultationScheduleEnum.settle_bill.getCode());
            if (payCount > 0) {
                throw new BusinessException("当前问诊单已补款");
            }
        }
        return createOrderPay(consultationSheet, uuid, type, payType);
    }

    @Transactional
    public Map<String, String> createOrderPay(ConsultationSheet consultationSheet, String uuid, Integer type, String payType) {
        Map<String, String> map = new HashMap<>();
        String orderNo = (LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS")) + UUIDUtil.S()).toUpperCase();
        orderNo = type.equals(BusinessEnum.AliPay.getType()) ? "ZFB" + orderNo : "WX" + orderNo;
        String payChannel = type.equals(BusinessEnum.AliPay.getType()) ? BusinessEnum.AliPay.getName() : BusinessEnum.WeiXinPay.getName();
        String desMsg = type.equals(BusinessEnum.AliPay.getType()) ? "支付宝充值" : "微信充值";
        Order userCapitalFlow = addUserCapitalFlow(uuid, consultationSheet, orderNo, payChannel, desMsg, payType);
        if (type.equals(BusinessEnum.AliPay.getType())) {
            if ("refund".equals(payType)) {
                refundAli(consultationSheet, userCapitalFlow);
                return null;
            }
            String aliPay = createAliPay(userCapitalFlow);
            map.put("orderNo", orderNo);
            map.put("sheetId", consultationSheet.getId() + "");
            map.put("data", aliPay);
            return map;

        } else if (type.equals(BusinessEnum.WeiXinPay.getType())) {
            JSONObject wxPay = createWxPay(userCapitalFlow);
            map.put("orderNo", orderNo);
            map.put("sheetId", consultationSheet.getId() + "");
            map.put("data", wxPay.toString());
            return map;
        }
        return null;
    }

    private void refundAli(ConsultationSheet consultationSheet, Order order) {

        Config lbSysSetting = configMapper.selectByCode(ConfigEnum.ALIPAY.getConfigType());
        JSONObject jsonObject = JSONObject.parseObject(lbSysSetting.getValue());
        AlipayClient alipayClient = new DefaultAlipayClient(jsonObject.getString("url"), jsonObject.getString("appId"), jsonObject.getString("appPrivateKey"), jsonObject.getString("format"),
                jsonObject.getString("charset"), jsonObject.getString("aliPayPublicKey"), jsonObject.getString("signType"));
        AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();
        AlipayTradeRefundModel model = new AlipayTradeRefundModel();
        //之前支付的订单号
        model.setOutTradeNo(consultationSheet.getOrderNo());
        model.setRefundAmount(order.getMoney().doubleValue() + "");
        model.setRefundReason("账单确认退款");

        //当前退款的订单号
        model.setOutRequestNo(order.getOrderId());
        request.setBizModel(model);
        AlipayTradeRefundResponse response = null;
        try {
            response = alipayClient.execute(request);
            final String fundChange = response.getFundChange();
            String tradeNo = response.getTradeNo();
            if ("Y".equals(fundChange)) {
                order.setPlatOrderId(tradeNo);
                order.setState(1);
                orderMapper.updateByPrimaryKeySelective(order);
                final ConsultationSheetSchedule consultationSheetSchedule = new ConsultationSheetSchedule();
                consultationSheetSchedule.setCreateTime(LocalDateTime.now());
                consultationSheetSchedule.setUpdateTime(LocalDateTime.now());
                consultationSheetSchedule.setConsultationSheetId(consultationSheet.getId());
                consultationSheetSchedule.setScheduleState(ConsultationScheduleEnum.end.getCode());
                final JSONObject valueJson = new JSONObject();
                valueJson.put("type", "refund");
                valueJson.put("money", order.getMoney());
                consultationSheetSchedule.setValueJson(valueJson.toJSONString());
                consultationSheetScheduleMapper.insertSelective(consultationSheetSchedule);
                consultationSheet.setScheduleState(ConsultationScheduleEnum.end.getCode());
                consultationSheetMapper.updateByPrimaryKeySelective(consultationSheet);
            } else {
                throw new BusinessException("当前订单已退款");
            }
        } catch (AlipayApiException e) {
            e.printStackTrace();
        }
    }


    public JSONObject createWxPay(Order order) {
        String byCode = configMapper.selectByCode(ConfigEnum.WXPAY.getConfigType()).getValue();
        JSONObject jsonObject = JSONObject.parseObject(byCode);
        String merchantId = jsonObject.getString("merchantId");
        String privateKey = jsonObject.getString("privateKey");
        String merchantSerialNumber = jsonObject.getString("merchantSerialNumber");
        String apiV3Key = jsonObject.getString("apiV3Key");
        String notifyUrl = jsonObject.getString("wxNotifyUrl");
        String appId = jsonObject.getString("appid");
        RSAAutoCertificateConfig config = new RSAAutoCertificateConfig.Builder()
                .merchantId(merchantId)
                .privateKey(privateKey)
                .merchantSerialNumber(merchantSerialNumber)
                .apiV3Key(apiV3Key)
                .build();
        AppService service = new AppService.Builder().config(config).build();
        PrepayRequest prepayRequest = new PrepayRequest();
        Amount amount = new Amount();
        prepayRequest.setAppid(appId);
        prepayRequest.setMchid(merchantId);
        prepayRequest.setDescription("医疗问诊单" + order.getDes());
        prepayRequest.setOutTradeNo(order.getOrderId());
        prepayRequest.setNotifyUrl(notifyUrl);
        amount.setTotal(order.getMoney().multiply(BigDecimal.valueOf(100)).intValue());
        amount.setCurrency("CNY");
        prepayRequest.setAmount(amount);
        PrepayResponse prepay = service.prepay(prepayRequest);


        JSONObject result = new JSONObject();
        result.put("appid", appId);
        result.put("partnerid", merchantId);
        result.put("prepayid", prepay.getPrepayId());
        result.put("package", "值Sign=WXPay");
        result.put("timestamp", System.currentTimeMillis() / 1000);
        return result;
    }


    private String createAliPay(Order order) {
        Config lbSysSetting = configMapper.selectByCode(ConfigEnum.ALIPAY.getConfigType());
        JSONObject jsonObject = JSONObject.parseObject(lbSysSetting.getValue());

        AlipayClient aliPayClient = new DefaultAlipayClient(jsonObject.getString("url"), jsonObject.getString("appId"), jsonObject.getString("appPrivateKey"), jsonObject.getString("format"),
                jsonObject.getString("charset"), jsonObject.getString("appPublicKey"), jsonObject.getString("signType"));
        //创建API对应的request类
        AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();
        AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
        //同步跳转地址，仅支持http/https
        request.setReturnUrl("");
        //对一笔交易的具体描述信息。如果是多种商品，请将商品描述字符串累加传给body
        model.setBody(order.getDes());
        //商品名称
        model.setSubject(order.getDes());
        ////商户订单号
        model.setOutTradeNo(order.getOrderId());
        //交易超时时间
        model.setTimeoutExpress("30m");
        //支付总金额
        model.setTotalAmount(order.getMoney().setScale(2, BigDecimal.ROUND_DOWN).toString());
        //销售产品码，商家和支付宝签约的产品码
        model.setProductCode("QUICK_MSECURITY_PAY");
        request.setBizModel(model);
        request.setNotifyUrl(jsonObject.getString("callBackUrl"));
        AlipayTradeAppPayResponse response = null;
        try {
            response = aliPayClient.sdkExecute(request);
        } catch (AlipayApiException e) {
            e.printStackTrace();
        }
        if (response.isSuccess()) {
            return response.getBody();
        } else {

            return null;
        }
    }


    private Order addUserCapitalFlow(String uuid, ConsultationSheet consultationSheet, String orderNo, String channel, String desMsg, String payType) {

        Order order = new Order();
        order.setCreateTime(LocalDateTime.now());
        order.setUpdateTime(LocalDateTime.now());
        order.setUuid(uuid);
        order.setOrganizationOperationUuid(consultationSheet.getOrganizationOperationUuid());
        order.setState(BusinessEnum.NO.getType());
        order.setConsultationSheetId(consultationSheet.getId());
        order.setPayChannel(channel);
        order.setDes(desMsg);
        order.setOrderId(orderNo);
        order.setName(consultationSheet.getConsultationType());
        String scheduleState = consultationSheet.getScheduleState();
        AtomicReference<BigDecimal> waitPrepayMoney = new AtomicReference<>(new BigDecimal(0));
        AtomicReference<BigDecimal> settleBillMoney = new AtomicReference<>(new BigDecimal(0));
        BigDecimal money = new BigDecimal(0);
        Map waitPrepayMap = new HashMap();
        Map settleBillMap = new HashMap();
        if (scheduleState.equals(ConsultationScheduleEnum.wait_prepay.getCode())) {
            String advancePaymentBillJson = consultationSheet.getAdvancePaymentBillJson();
            waitPrepayMap = JSONObject.parseObject(advancePaymentBillJson, Map.class);
            waitPrepayMap.forEach((k, v) -> {
                BigDecimal bigDecimal = new BigDecimal(v.toString());
                waitPrepayMoney.set(waitPrepayMoney.get().add(bigDecimal));
            });
            money = waitPrepayMoney.get();
            order.setPayType("pay");
        } else if (scheduleState.equals(ConsultationScheduleEnum.settle_bill.getCode())) {
            String advancePaymentBillJson = consultationSheet.getAdvancePaymentBillJson();
            waitPrepayMap = JSONObject.parseObject(advancePaymentBillJson, Map.class);
            waitPrepayMap.forEach((k, v) -> {
                BigDecimal bigDecimal = new BigDecimal(v.toString());
                waitPrepayMoney.set(waitPrepayMoney.get().add(bigDecimal));
            });
            settleBillMap = JSONObject.parseObject(consultationSheet.getUltimatelyBillJson(), Map.class);
            if (settleBillMap != null) {
                settleBillMap.forEach((k, v) -> {
                    BigDecimal bigDecimal = new BigDecimal(v.toString());
                    settleBillMoney.set(settleBillMoney.get().add(bigDecimal));
                });
            }
            // 预付金额小于最终金额 需要补款
            if (waitPrepayMoney.get().subtract(settleBillMoney.get()).compareTo(BigDecimal.ZERO) <= 0) {
                if (BusinessEnum.refund.getName().equals(payType)) {
                    throw new BusinessException("预付金额小于最终金额 需要补款");
                }
                money = settleBillMoney.get().subtract(waitPrepayMoney.get());
                order.setPayType("pay");
            }
            // 预付金额大于最终金额 需要退款
            if (waitPrepayMoney.get().subtract(settleBillMoney.get()).compareTo(BigDecimal.ZERO) > 0) {
                if (BusinessEnum.pay.getName().equals(payType)) {
                    throw new BusinessException("预付金额小于最终金额 需要退款");
                }
                money = waitPrepayMoney.get().subtract(settleBillMoney.get());
                order.setPayType("refund");
            }
        }

        order.setScheduleState(scheduleState);
        order.setMoney(money);
        orderMapper.insertSelective(order);
        return order;

    }

    /**
     * 订单支付成功回调
     *
     * @param orderNo
     * @return
     */
    @Transactional
    public boolean paySuccess(String orderNo, String zfbTradeNo) {
        Order order = orderMapper.getByOrderNo(orderNo);
        if (order == null) {
            return false;
        }
        if (BusinessEnum.YES.getType().equals(order.getState())) {
            return false;
        }
        Integer consultationSheetId = order.getConsultationSheetId();
        ConsultationSheet consultationSheet = consultationSheetMapper.selectByPrimaryKey(consultationSheetId);
        if (consultationSheet == null) {
            return false;
        }
        String scheduleState = consultationSheet.getScheduleState();
        // 只有待结算和待预付才添加对应的进度
        if (ConsultationScheduleEnum.wait_prepay.getCode().equals(scheduleState) || ConsultationScheduleEnum.settle_bill.getCode().equals(scheduleState)) {
            ConsultationSheetSchedule consultationSheetSchedule = new ConsultationSheetSchedule();
            consultationSheetSchedule.setCreateTime(LocalDateTime.now());
            consultationSheetSchedule.setUpdateTime(LocalDateTime.now());
            consultationSheetSchedule.setConsultationSheetId(consultationSheetId);
            // 当前是等待预付
            if (ConsultationScheduleEnum.wait_prepay.getCode().equals(consultationSheet.getScheduleState())) {

                consultationSheetSchedule.setScheduleState(ConsultationScheduleEnum.have_prepay.getCode());
                // 保存当前账单的订单  方便进行退款
                consultationSheet.setOrderNo(orderNo);
            } else if (ConsultationScheduleEnum.settle_bill.getCode().equals(consultationSheet.getScheduleState())) {
                final JSONObject valueJson = new JSONObject();
                valueJson.put("type", "pay");
                valueJson.put("money", order.getMoney());
                consultationSheetSchedule.setValueJson(valueJson.toJSONString());
                consultationSheetSchedule.setScheduleState(ConsultationScheduleEnum.end.getCode());
            }
            consultationSheet.setScheduleState(consultationSheetSchedule.getScheduleState());
            consultationSheetScheduleMapper.insertSelective(consultationSheetSchedule);
            consultationSheetMapper.updateByPrimaryKeySelective(consultationSheet);
        }
        // 修改订单支付结果
        order.setPlatOrderId(zfbTradeNo);
        order.setState(BusinessEnum.YES.getType());
        orderMapper.updateByPrimaryKeySelective(order);
        return true;
    }

    public boolean queryByOrder(String orderNo) {
        final Order byOrderNo = orderMapper.getByOrderNo(orderNo);
        if (byOrderNo == null) {
            throw new BusinessException("订单不存在");
        }
        return byOrderNo.getState().equals(BusinessEnum.YES.getType());
    }
}
